import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { Subject } from 'rxjs/internal/Subject';
import { ApiService } from '@core/services/http/api.service';
import { Events } from '@core/services/events/Events';
import { ExpressionService } from '@core/services/expression/ExpressionService';
import { SubjectService } from '@core/services/events/subject.service';
import { SearchService } from '@core/services/search.service';
import { catchError, debounceTime, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { fillField, getO, missing } from '../../../utils/utils';
import { of } from 'rxjs/internal/observable/of';
import { NgSelectComponent } from '@shared/apes/ng-select';
import { ApesDialogTransferComponent } from '@shared/dialog-transfer';

@Component({
  selector: 'apes-ng-select',
  template: `
    <ng-select #ngSelectRef
               bindLabel="name"
               [items]="itemsData"
               [(ngModel)]="_selectData"
               [typeahead]="contractInput$"
               [disabled]="disabled"
               [loading]="contractLoading"
               [placeholder]="placeholder"
               [multiple]="multiple"
               [closeOnSelect]="closeOnSelect"
               [appendTo]="appendTo"
               [dropdownPosition]="dropdownPosition"
               (change)="backData($event)"
               (search)="searchData($event)"
               [groupBy]="showGroupBy(itemsData)"
               [searchBtn]="_searchBtn"
               (searchBtnFn)="onSearch()"
               (scrollToEnd)="fetchMore()">
      <!-- 表头部分 -->
      <ng-template ng-label-tmp let-item="item" let-clear="clear">
        <span class="ng-value-label">{{formatter(item, getValueFormatter())}}</span>
        <span class="ng-value-icon right" (click)="clear(item)" aria-hidden="true">×</span>
      </ng-template>

      <ng-template ng-optgroup-tmp let-item="item" let-index="index">
        <ng-container>
          {{item[groupBy]}}
        </ng-container>
      </ng-template>

      <ng-template ng-option-tmp let-item="item">
        <div class="ng-option-flex">
          <span class="ng-value-label">{{formatter(item, getDownFormatter())}}</span>
          <span *ngIf="showRight && !showRightIcon" style="text-align: right">
            {{formatter(item, getDownRightFormatter())}}
          </span>
          <span *ngIf="showRight && showRightIcon" style="text-align: right">
            <i apes-icon
               class="apes-ng-select-showRightIcon"
               [apesType]="formatter(item, getDownRightIconFormatter())"
               (click)="clickIcon($event,item)"></i>
          </span>
        </div>
      </ng-template>

    </ng-select>
  `,
  styles: [`
    .ng-option-flex {
      display: flex;
      line-height: 15px;
    }

    .ng-option-flex span {
      flex: 1;
    }
  `],
})
export class ApesNgSelectComponent implements OnInit, AfterViewInit {

  itemsData: any[] = [];

  @Input() searchSelectApi: string;
  _domain: any;
  formState: any;

  _searchBtn = false;

  private _current = 0;
  private _size = 20;
  private _pages = 0;
  private _searchText = '';
  contractLoading = false;
  contractInput$ = new Subject<string>();

  @Input() valueFormatter: any;
  @Input() downFormatter: any;
  @Input() downRightFormatter: any;
  @Input() downRightIconFormatter: any;
  @Input() showRight = false;
  @Input() showRightIcon = false;
  @Input() disabled : boolean = false;
  @Input() init = false;
  @Input() appendTo: any;
  @Input() placeholder: any;
  @Input() searchFormId: string;
  @Input() multiple = false;

  @Input() closeOnSelect: boolean = true;
  @Input() dropdownPosition: any;

  @Input()
  set selectData(value: any) {
    this._selectData = value;
    if (this.itemsData.length == 0 && this._selectData) {
      this.itemsData.push(this._selectData);
    }
  }

  get selectData() {
    return this._selectData;
  }

  @Input()
  set domain(value: any) {
    this._domain = value;
  }

  get domain() {
    return this._domain;
  }

  _selectData: any;
  groupBy: any;

  @ViewChild('ngSelectRef', { static: false }) ngSelectComponent: NgSelectComponent;


  @Output('change') changeEvent = new EventEmitter();
  @Output('search') searchEvent = new EventEmitter();
  @Output('iconChange') iconChangeEvent = new EventEmitter();


  constructor(protected apiService: ApiService, protected events: Events, protected expressionService: ExpressionService,
              protected subject: SubjectService, protected searchService: SearchService) {
    this.loadContract();
  }

  ngOnInit() {
  }

  ngAfterViewInit(): void {

    if (this.init) {
      this.initItemData();
      this.getItemData();
    }
    if (this.searchFormId) {
      this._searchBtn = true;
    }
  }

  private loadContract() {
    this.contractInput$.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      tap(() => this.contractLoading = true),
      switchMap((term) => {
        if (missing(term)) {
          this.contractLoading = false;
          return of([]);
        }
        if (this._searchText === term) return of([]);
        this._current = 0;
        this._searchText = term;
        const data = this.getApiParams();

        return this.apiService.post(this.searchSelectApi, data).pipe(
          map((res) => {
              this.itemsData = [];
              this._searchText = term;
              this.itemsData = this.itemsData.concat(res.content);
              this._current = res.page + 1;
              this._pages = res.pages;
              return res.content;
            },
          ),
          catchError(() => of([])), // empty list on error
          tap(() => this.contractLoading = false),
        );
      }),
    ).subscribe();
  }

  fetchMore() {
    this.contractLoading = true;
    // using timeout here to simulate backend API delay
    if (this._current === this._pages) {
      this.contractLoading = false;
      return;
    }
    this.getItemData();
  }

  initItemData() {
    this._current = 0;
    this._pages = 0;
    this.itemsData = [];
  }

  close() {
    this.ngSelectComponent.close();
  }

  getItemData() {
    const data = this.getApiParams();
    this.apiService.post(this.searchSelectApi, data).subscribe(item => {
      this.contractLoading = false;
      if (!item) return;
      if (item.page === 0) {
        this.itemsData = [];
      }
      this.itemsData = this.itemsData.concat(item.content);
      this._current = item.page + 1;
      this._pages = item.pages;
      // 设置默认值
      // if (this.defaultValue) {
      //   let tempVal = this.itemsData.filter(item => item.id === this.defaultValue.id);
      //   if (tempVal && tempVal.length == 0) {
      //     this.itemsData.unshift(this.defaultValue);
      //     this.selectedItem = this.multiple ? [this.defaultValue] : this.defaultValue;
      //   } else {
      //     this.selectedItem = this.multiple ? tempVal : tempVal[0];
      //   }
      // }
    });
  }

  onSearch() {
    const options = { multiple: this.multiple }; // 声明查询条件
    if (this._domain) {
      const group = fillField(this._domain, this.formState, true, this._searchText);
      if (!(group.filters.length === 0 && group.groups.length === 0)) {
        options['domain'] = group;
      }
    }
    // 弹窗api与下拉api保持一致
    if (this.searchSelectApi) options['api'] = this.searchSelectApi;
    options['items'] = [...this.itemsData];
    options['valueFormatter'] = this.getValueFormatter();
    options['value'] = this._selectData;
    this.searchService.openDialog(this.searchFormId, options, (result: any) => {
      if (result && result.length > 0) {
        this.itemsData = this.getUniqueArray([...this.itemsData, ...result], 'id');
        if (this.multiple) {
          this.backData(result);
        } else {
          this.backData(result[0]);
        }
      } else if (result && result instanceof Object) {
        this.backData(result);
      }
    }, null).subscribe();
  }

  getApiParams() {
    const data = { key: this._searchText, page: this._current, size: this._size };
    if (this._domain) {
      const group = fillField(this._domain, this.formState, false, this._searchText);
      data['group'] = group;
    }
    return data;
  }

  // 获取不重复项数组
  private getUniqueArray(array: any[], id: string): any[] {
    array = Array.from(new Set(array));
    if (typeof array[0] === 'object') {
      const res = new Map();
      array = array.filter((a) => !res.has(a[id]) && res.set(a[id], 1));
    }
    return array;
  }

  getValueFormatter(): string {
    return this.valueFormatter || 'value.name';
  }

  getDownFormatter(): string {
    return this.downFormatter || '\'[ \' + value.id + \' ]\' + \' \' + value.name';
  }

  getDownRightFormatter() {
    return this.downRightFormatter || '\'[ \' + value.id + \' ]\' + \' \' + value.name';
  }

  getDownRightIconFormatter() {
    return this.downRightIconFormatter;
  }

  formatter(value: any, expression: string) {
    return this.expressionService.evaluate(expression, {
      // context: this.formState,
      // model: this.formState.model,
      value: value,
    });
  }

  showGroupBy(items) {
    let check = false;
    let groupBy = '';
    if (items != undefined && items.length != 0) {
      items.forEach((item) => {
        if (item && item.hasOwnProperty('groupBy')) {
          check = true;
          groupBy = item.groupBy;
        }
      });
    }
    if (check) {
      this.groupBy = groupBy;
      return groupBy;
    } else {
      return false;
    }
  }

  clickIcon(e, items) {
    e.stopPropagation();
    e.preventDefault();
    this.iconChangeEvent.emit(items);
  }

  backData(value) {
    this.changeEvent.emit(value);
  }

  searchData(param) {
    let searchText = param.term;
    this.searchEvent.emit(searchText);
  }

  test(value) {
    console.log('test------', value);
  }
}
